const { rollup } = require('rollup')
const path = require('path')
const resolve = require('rollup-plugin-node-resolve')
const vue = require('rollup-plugin-vue')
const { babel } = require('@rollup/plugin-babel')
const commonjs = require('@rollup/plugin-commonjs')
const webpackSources = require('webpack-sources')

// 打包好的bundle
const bundleCache = {}

export default api => {
  api.register({
    key: 'dumi.registerCompiletime',
    fn: () => ({
      // 编译时名称，唯一
      name: 'plugin-dumi-vue',
      // demo 渲染组件
      component: path.join(__dirname, 'previewer.js'),
      // demo 语法树节点编译函数
      transformer: (
        // 入参，类型：
        // {
        //   attrs: Record<string, any>,   code 标签的属性
        //   mdAbsPath: string,            当前 Markdown 文件的路径
        //   node: mdASTNode,              语法树节点
        // }
        opts,
      ) => {
        // 从其他技术栈工具中获取 demo 渲染需要的内容，例如 dev server 的产物地址等
        // 出参类型参考：https://github.com/umijs/dumi/blob/master/packages/preset-dumi/src/transformer/remark/previewer/builtin.ts#L50
        // 取出文件路径
        const { mdAbsPath, node } = opts
        // 项目的根路径
        const projectDir = path.resolve('./')
        // dumi的路径
        const dumiDir = path.join(api.paths.absTmpPath, 'dumi')
        // vue文件的相对路径
        const vueRelativeDir = path.relative(dumiDir, mdAbsPath).replace(/\.\.\//g, '').replace('.md', '.vue')
        // 代码路径
        const { lang, source, meta } = node.properties
        // 源代码路径
        let originCodeDir = mdAbsPath
        // 如果当前是vue
        if (lang === 'html') {
          // 将vue代码冲readme中抽离出来成立的vue文件路径
          originCodeDir = path.join(dumiDir, 'vue', vueRelativeDir)
          // 写入文件中
          api.writeTmpFile({
            path: path.join('dumi', 'vue', vueRelativeDir),
            content: source
          })
          // 创建vue的bundle
          buildVueBundle(originCodeDir)
          console.log('originCodeDir: ', originCodeDir)
        }
        return {
          // 传递给主题预览组件 Previewer 的 props
          previewerProps: {
            // 需要展示的源代码
            sources: {
              _: { path: originCodeDir },
            }
          },
          // demo 渲染器的 props，会传递给上面注册的渲染器组件
          rendererProps: {
            path: vueRelativePath(originCodeDir)
          },
        };
      },
    }),
  });
  // 拷贝静态资源
  api.chainWebpack(config => {
    config.plugin('copy-process-assets-plugin').use(CopyProcessAssetsPlugin)
    return config
  })
}

/**
 * 创建vue文件的bundle
 * @param {String} originCodeDir 源代码的路径
 */
async function buildVueBundle(originCodeDir) {
  // 开始打包
  const bundle = await rollup({
    input: originCodeDir,
    plugins: [
      resolve(),
      vue({ css: true, compileTemplate: true }),
      babel({ babelHelpers: 'external', exclude: '**/node_modules/**' }),
      commonjs()
    ],
    external: ['vue']
  })
  // 输出配置
  const outputConfig = {
    file: path.join(originCodeDir, '../', 'index.es.js'),
    format: 'esm',
    name: 'plugin-dumi-vue',
    globals: {
      vue: 'window.Vue'
    },
  }
  // 生成code和sourcemap
  const { output } = await bundle.generate(outputConfig)
  // 输出到文件
  await bundle.write(outputConfig)
  // 获取输出结果
  output.forEach(o => {
    // 将bundle按照路径保存到内存中
    bundleCache[vueRelativePath(o.facadeModuleId)] = o.code
  })
  console.log('bundleCache: ', bundleCache)
}

/**
 * 将vue转换成js文件
 */
function vueRelativePath(path) {
  // vue路径在文件中的位置
  const vuePathIndex = path.indexOf('/vue')
  // 返回路径
  return path.slice(vuePathIndex).replace('.vue', '.es.js')
}

class CopyProcessAssetsPlugin {
  apply(compiler) {
    compiler.hooks.thisCompilation.tap('copy-process-assets', compilation => {
      compilation.hooks.additionalAssets.tapAsync('copy-process-assets-plugin', callback => {
        Object.keys(bundleCache).forEach(key => {
          compilation.assets[key.slice(1)] = new webpackSources.RawSource(bundleCache[key])
          console.log(key.slice(1), new webpackSources.RawSource(bundleCache[key]))
        })
        callback()
      })
    })
  }
}